﻿#pragma once
namespace LK
{
	//转换为ToString输出
	std::ostream& operator<<(std::ostream&o, Object const&x);
	//转换为ToString输出
	std::wostream& operator<<(std::wostream&o, Object const&x);
	//转换为ToString输出
	std::ostream& operator<<(std::ostream&o, PObject const&x);
	//转换为ToString输出
	std::wostream& operator<<(std::wostream&o, PObject const&x);

	/**@brief 基类*/
	struct Object
	{
		/**
		@brief 用以推断被代理类型*/
		typedef Object IObj;
		/**
		@brief 用以推断代理类型*/
		typedef PObject PObj;
		//引用计数
		mutable PRefCount RefCount;
		/**@brief 默认构造函数*/
		Object() {}
		/**@brief 复制构造函数*/
		Object(const Object&obj) :RefCount() {}
		/**@brief 赋值函数*/
		Object& operator=(const Object&) { return *this; }
		/**@brief 交换两个对象*/
		virtual void SwapWith(Object&);
		/**@brief 判断两者是否相等*/
		virtual bool Equals(PObject obj)const;
		/**@brief 输出该对象的字符串描述*/
		inline virtual PString ToString()const;
		/**@brief 析构函数*/
		virtual ~Object() { if (RefCount != -1)RefCount.pCount->referenceCount &= MinValues::template Of<index_t>(); }
		/**@brief 获取该对象的类型*/
		virtual PType GetType()const;
		/**@brief 交换两个变量的值，使用operator=*/
		template<class T>static inline void Swap(T& t1, T& t2)
		{
#ifdef SUPPORT_RIGHT_VALUE_REFERENCE
			T t = (T&&)t1; t1 = (T&&)t2; t2 = (T&&)t;
#else
			T t = t1; t1 = t2; t2 = t;
#endif
		}
		/**@brief 交换两个引用*/
		static inline void SwapReference(PObject& p1, PObject&p2);
		/**
		@brief 获取该对象表示的数据地址
		@note 该方法用以配合MObject进行封装抽象:如果该类型不是MObject，则直接返回NULL，否则返回MObject中所表示对象的地址*/
		virtual void* GetObjectPtr() { return NULL; }
	private:
		friend PObject;
	};

	/**@brief 引用类型基类*/
	struct PObject
	{
		/**
		@brief 用以推断被代理类型*/
		typedef Object IObj;
		/**
		@brief 用以推断代理类型*/
		typedef PObject PObj;
		/**
		@brief 默认构造函数*/
		inline PObject() :pObject(NULL) {}
		/**
		@brief 析构函数*/
		inline ~PObject() { if (--RefCount == 0)delete pObject; }
		/**
		@brief 复制构造函数*/
		inline PObject(PObject const&t) :RefCount(t.RefCount), pObject((Object*)t.pObject) { if (RefCount <= 0)pObject = NULL; if (pObject)++(RefCount); }
		/**
		@brief 从除了PObject外的任意其它类型对象构造
		@param [模板]参数T不需要指定
		@note 构造规则：
		@note 1.如果对象是PObject的派生类，则直接引用增加；
		@note 2.如果对象可以隐式转换到Object*，那么将直接将Object*指向的对象进行引用；
		@note 3.如果对象是其它类型将进行MObject封箱操作*/
		template<typename TT>inline PObject(TT const&t) :pObject(NULL) { Ctor<TT>(t); }
		/**
		@brief 赋值*/
		inline PObject& operator=(PObject const&r) { SetTo(r.pObject); return *this; }
		/**
		@brief 获取对象指针，并在对象指针为空时抛出异常*/
		inline Object* Ptr()const { if (RefCount <= 0)Throw<PPointerIsNullException>(); if (!pObject)Throw<PPointerIsNullException>(); return pObject; }
		/**
		@brief 获取对象指针*/
		inline Object* GetPtr()const { if (RefCount <= 0)return pObject = NULL; return pObject; }
		/**
		@brief 重载operator->*/
		inline Object* operator->()const { return Ptr(); }
		/**
		@brief 将该引用设置为空引用*/
		inline void SetToNull()
		{
			if (pObject)
			{
				index_t cnt = --RefCount;
				if(cnt == 0) { delete pObject; }
				else RefCount.ChangeRef(NULL);
			}
			pObject = NULL;
		}
		/**
		@brief 将该引用设置为另一个指针，但必须小心保证这个指针是可用的*/
		void SetTo(Object const*p)
		{
			if (p == pObject)return;
			if (pObject)
			{
				index_t cnt = --RefCount;
				if (cnt == 0) { delete pObject; }
				else RefCount.ChangeRef(NULL);
			}
			if (p) {
				if (p->RefCount == -1 && RefCount.GetWeakRefCount() == 1)//对象从未被引用而且引用计数可重用
					p->RefCount = ++RefCount;
				else RefCount = ++(p->RefCount);
			}
			pObject = (Object*)p;
		}
		/**
		@brief 判断是否是空引用*/
		inline bool IsNull()const { return RefCount <= 0; }
		/**
		@brief 判断两个代理对象是否相等*/
		inline bool Equals(PObject t)const { if (pObject == t.pObject)return true; if (RefCount <= 0 || t.RefCount <= 0)return false; return pObject->Equals(t); }
		/**
		@brief 判断两个代理对象是否引用了同一个对象*/
		static inline bool ReferenceEqual(PObject t1, PObject t2) { return t1.pObject == t2.pObject; }
		/**
		@brief 获取该对象的字符串表示*/
		inline PString ToString()const;
		/**
		@brief 获取对象类型*/
		inline PType GetType()const;
		/**
		@brief 判断两个对象的类型是否相等*/
		static bool TypeEqual(PObject t1, PObject t2);
		/**
		@brief 添加类型信息*/
		static void AddTypeInfo();
		/**
		@brief 交换引用*/
		static void SwapReference(PObject& p1, PObject&p2) { Object::Swap(p1.RefCount.pCount, p2.RefCount.pCount); Object::Swap(p1.pObject, p2.pObject); }
		/**
		@brief 制作一个指定类型的PObject对象*/
		template<typename T>inline static PObject MakeObject(T t);
		/**
		@brief 转换到指定的代理，使用动态类型信息转换，转换失败时返回空引用，不抛出异常*/
		template<class T>typename UseIf<IsBaseOf<PObject, T>::V&&IsSame<T, typename RemoveAllExtents<T>::T>::V, T>::T CastTo()const { return CastTo1 <T>(); }
		/**
		@brief 转换到指定的类型，使用动态类型信息转换，转换失败时抛出异常*/
		template<class T>typename UseIf<!IsBaseOf<PObject, T>::V || !IsSame<T, typename RemoveAllExtents<T>::T>::V, T>::T CastTo()const { return CastTo2 <T>(); }
		/**
		@brief 动态调用函数（需要Reflection支持，若未启用Reflection将抛出异常）*/
		PObject DynamicCall(PString const&functionName, Collection::PArray<PObject>const&args)const;
#ifdef SUPPORT_VARIADIC_TEMPLATES
		template<class ReturnType, class ...ArgumentsType>ReturnType DynamicCall(PString const&functionName, ArgumentsType...args)const;
#else
#define TMPF(x)\
		template<class ReturnType MF3_RepeatAsc(MF1__CT,2,x)> ReturnType DynamicCall(PString const&functionName MF3_RepeatAsc(MF1__T,2,x))const;
		MF2_Repeatb(TMPF, 30)
#undef TMPF
#endif
			/**@brief 引用计数*/
			PRefCount RefCount;
	private:
		//被代理对象指针
		mutable Object* pObject;
		/**
		@brief 构造方法
		@note 当对象可隐式转换到Object*或者是PObject的派生类时，进行转换代理，否则进行封箱*/
		template<class T>inline typename UseIf<IsBaseOf<PObject, T>::V>::T Ctor(T const&t) { SetTo(((PObject&)t).pObject); }
		template<class T>inline typename UseIf<IsConvertible<T, Object const*>::V>::T Ctor(T const&t) { SetTo((Object*)t); }
		template<class T>inline typename UseIf<!IsBaseOf<PObject, T>::V&&!IsConvertible<T, Object const*>::V>::T Ctor(typename AddConst<typename AddLValueReference<T>::T>::T t) { Ctor2 <T>(t); }
		template<class T>void Ctor2(typename AddConst<typename AddLValueReference<T>::T>::T t);
		template<class T>T CastTo1()const;
		template<class T>T CastTo2()const;
		template<class T>friend struct WeakPtr;
	};
	inline void Object::SwapReference(PObject&p1, PObject&p2) { PObject::SwapReference(p1, p2); }
	inline bool Object::Equals(PObject obj)const { return this == obj.GetPtr(); }
	inline bool operator==(PObject p1, PObject p2) { return p1.Equals(p2); }
	inline bool operator!=(PObject p1, PObject p2) { return !p1.Equals(p2); }
}
